package analyser;

import analyser.dispatcher.INodeAnalyseDispatcher;
import analyser.node.INodeAnalyser;
import analyser.node.MibObjectIdentifierAnalyser;
import analyser.dispatcher.NodeAnalyseDispatcher;
import analyser.dispatcher.NodeAnalyseDispatcherV1;
import analyser.dispatcher.NodeAnalyseDispatcherV2;
import exception.MibException;
import kit.AnalyserIndexerKit;
import kit.LetterIndexer;
import linker.INodeLinker;
import linker.MibNodeLinker;
import manager.IMibManager;
import model.MibModule;
import model.MibNode;
import util.common.CharType;
import util.common.CharTypeChecker;
import util.common.MibKeys;
import util.common.StringUtils;

import java.util.*;

public class MibSyntaxAnalyzer implements IMibSyntaxAnalyzer {
    private static final String SMI_V2 = "SNMPv2-SMI";

    /**
     * 存储初步分离开的字符串模块
     */
    private class ModuleBean{
        public String content;
        public String importsModule;
        public String exportsModule;
        public List<String> nodeModules = new ArrayList<>();
        public int indexer = 0;

        public String getNextNodeModule(){
            if (hasNextNodeModule()){
                return nodeModules.get(indexer++);
            }else{
                return null;
            }
        }

        public boolean hasNextNodeModule(){
            return indexer  < nodeModules.size();
        }
    }

    private List<String> txtList = new ArrayList<>();
    private MibModule module = null;
    private ModuleBean bean = null;
    private IMibManager manager;
    private INodeLinker linker;
    private INodeAnalyseDispatcher dispatcher;

    public MibSyntaxAnalyzer(IMibManager manager) {
        this(manager, new MibNodeLinker(manager));
    }

    public MibSyntaxAnalyzer(IMibManager manager, INodeLinker linker) {
        this.manager = manager;
        this.linker = linker;
        NodeAnalyseDispatcher dispatcher = new NodeAnalyseDispatcherV1(txtList);
        dispatcher = new NodeAnalyseDispatcherV2(dispatcher);
        this.dispatcher = dispatcher;
    }

    public MibSyntaxAnalyzer(IMibManager manager, INodeAnalyseDispatcher dispatcher) {
        this(manager, new MibNodeLinker(manager), dispatcher);
    }

    public MibSyntaxAnalyzer(IMibManager manager, INodeLinker linker, INodeAnalyseDispatcher dispatcher) {
        this.manager = manager;
        this.linker = linker;
        this.dispatcher = dispatcher;
    }

    public MibModule analyse(String txt){
        this.module = new MibModule();
        this.bean = new ModuleBean();
        txt = RemoveAnnotationAndEmptyLine(txt);
        txt = ReplaceTextContent(txt);
        txt = TransferWhiteCharacterToSpace(txt);
        this.bean.content = txt;
        analyseOutline();
        analyseImportsModule();
        analyseNodeModules();
        this.linker.linkNodes(this.module);
        for(MibNode node : this.module.nodes.values()){
            node.moduleName = this.module.moduleName;
        }
        return this.module;
    }

    /**
     * 分析mib文件的整体框架，提取出各个模块
     * 包括提取mib库名，IMPORT模块，EXPORTS模块，及各个节点模块。
     * @return
     */
    public void analyseOutline(){
        LetterIndexer indexer = new LetterIndexer(bean.content);
        AnalyserIndexerKit kit = new AnalyserIndexerKit(indexer);
        int state = 0;
        int fail;
        String errorInfo = "解析外部结构异常。";
        String str = "";
        String temp;
        while (state != 99){
            switch (state) {
                case 0:
                    str = indexer.getNextToSpaceIgnoreLeftSpace();
                    if (StringUtils.isNotNullOrWhiteSpace(str)) {
                        module.moduleName = str;
                        state = 1;
                    } else {
                        state = -state;
                        errorInfo = "解析异常：获取不到模块名！";
                    }
                    break;
                case 1:
                    state = kit.nextMatch(MibKeys.DEFINITIONS, 2, -state);
                    break;
                case 2:
                    state = kit.nextMatch(MibKeys.ASSIGNMENT, 3, -state);
                    break;
                case 3:
                    state = kit.nextMatch(MibKeys.BEGIN, 4, -state);
                    break;
                case 4:
                    if ((state = kit.nextMatch(MibKeys.IMPORTS, 5, -state)) == 5) {
                        str = MibKeys.IMPORTS;
                    } else if ((state = kit.nextMatch(MibKeys.EXPORTS, 7, -state)) == 7) {
                        str = MibKeys.EXPORTS;
                    }else if ((state = kit.nextMatch(MibKeys.END, 99, -state)) == 99){
                    }else {
                        char c = indexer.probeNextCharIgnoreLeftSpace();
                        if (CharTypeChecker.isSmallLetter(c)) { // 小写字母
                            state = 9;
                        } else if (CharTypeChecker.isCapitalLetter(c)) { // 大写字母
                            state = 12;
                        }
                        str = indexer.getNext(CharType.Letter, CharType.Hyphen, CharType.Number);
                        if (StringUtils.isNullOrWhiteSpace(str)){  // 获取不到节点名
                            state = -state;
                        }
                    }
                    break;
                case 5:
                    str += indexer.getNextUntilSpecifiedTypes(CharType.Semicolon);
                    state = 6;
                    break;
                case 6:
                    str += indexer.getNextIgnoreLeftSpace(1);
                    bean.importsModule = str;
                    state = 4;
                    break;
                case 7:
                    str += indexer.getNextUntilSpecifiedTypes(CharType.Semicolon);
                    state = 8;
                    break;
                case 8:
                    str += indexer.getNext(1);
                    bean.exportsModule = str;
                    state = 4;
                    break;
                case 9:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypesIgnoreLeftSpace(MibKeys.ASSIGNMENT);
                    str += " " + temp;
                    state = 10;
                    break;
                case 10:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypesIgnoreLeftSpace(CharType.RBrace);
                    if (StringUtils.isNotNullOrWhiteSpace(temp)){
                        str += temp;
                        state = 11;
                    }else{
                        state = -state;
                    }
                    break;
                case 11:
                    bean.nodeModules.add(str);
                    printModule(str);
                    state = 4;
                    break;
                case 12:
                    // str 目前处于获取到节点名状态
                    if ((state = kit.nextMatch(MibKeys.MACRO, 13, -12)) == 13){
                        str += " " + MibKeys.MACRO;
                    } else if ((state = kit.nextMatch(MibKeys.ASSIGNMENT, 17, -12)) == 17) {
                        str += " " + MibKeys.ASSIGNMENT;
                    } else if ((state = kit.nextMatch(MibKeys.OBJECT, 33, -12)) == 33){
                        if ((state = kit.nextMatch(MibKeys.IDENTIFIER, 33, -12)) == 33){
                            str += " " + MibKeys.OBJECT + " " + MibKeys.IDENTIFIER;
                        }
                    }
                    break;
                case 13:
                    if ((state = kit.nextMatch(MibKeys.ASSIGNMENT, 14, -state)) == 14){
                        str += " " + MibKeys.ASSIGNMENT;
                    }
                    break;
                case 14:
                    if ((state = kit.nextMatch(MibKeys.BEGIN, 15, -state)) == 15){
                        str += " " + MibKeys.BEGIN;
                    }
                    break;
                case 15:
                    temp = indexer.getNextUntilSpecifiedTypesIgnoreLeftSpace(MibKeys.END);
                    if ((state = kit.notNull(temp, 16, -state)) == 16){
                        str += temp;
                    }
                    break;
                case 16:
                    indexer.getNextIgnoreLeftSpace(MibKeys.END.length());
                    str += " " + MibKeys.END;
                    bean.nodeModules.add(str);
                    printModule(str);
                    state = 4;
                    break;
                case 17:
                    fail = -state;
                    if ((state = kit.nextMatch(MibKeys.SEQUENCE, 18, fail)) == 18){
                        str += " "  + MibKeys.SEQUENCE;
                    }else if ((state = kit.nextMatch(MibKeys.CHOICE, 21, fail)) == 21){
                        str += " "  + MibKeys.CHOICE;
                    }else if ((state = kit.nextMatch(MibKeys.TEXTUAL_CONVENTION, 30, fail)) == 30){
                        str += " "  + MibKeys.TEXTUAL_CONVENTION;
                    }else if ((state = kit.nextMatch("[", 24, fail)) == 24){
                        str += " [";
                    }else{
                        // 检测类型
                        temp = kit.matchSyntaxType();
                        if ((state = kit.notNull(temp, 27, fail)) == 27){
                            str += " " + temp;
                        }
                    }
                    break;
                case 18:
                    if ((state = kit.nextMatch("{", 19, -state)) == 19){
                        str += " {";
                    }
                    break;
                case 19:
                    temp = indexer.getNextUntilSpecifiedTypesIgnoreLeftSpace(CharType.RBrace);
                    if (StringUtils.isNotNullOrWhiteSpace(temp)){
                        str += temp;
                        state = 20;
                    }else {
                        state = -state;
                    }
                    break;
                case 20:
                    indexer.getNextIgnoreLeftSpace(1);
                    str += "}";
                    bean.nodeModules.add(str);
                    printModule(str);
                    state = 4;
                    break;
                case 21:
                    if ((state = kit.nextMatch("{", 22, -state)) == 22){
                        str += " {";
                    }
                    break;
                case 22:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypesIgnoreLeftSpace(CharType.RBrace);
                    if (StringUtils.isNotNullOrWhiteSpace(temp)){
                        str += temp;
                        state = 23;
                    }else {
                        state = -state;
                    }
                    break;
                case 23:
                    bean.nodeModules.add(str);
                    printModule(str);
                    state = 4;
                    break;
                case 24:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypesIgnoreLeftSpace(CharType.RSquareBracket);
                    if (StringUtils.isNotNullOrWhiteSpace(temp)) {
                        str += " " + temp;
                        state = 25;
                    }else{
                        state = -state;
                    }
                    break;
                case 25:
                    if ((state = kit.nextMatch(MibKeys.IMPLICIT, 26, -state)) == 26){
                        str += " " + MibKeys.IMPLICIT;
                    }
                    break;
                case 26:
                    // 检测类型
                    temp = kit.matchSyntaxType();
                    if (temp != null){
                        str += " " + temp;
                        state = 27;
                    }else{
                        state = -state;
                    }
                    break;
                case 27:
                    if ((state = kit.nextMatch("(", 28, 4)) == 28){
                        str += " (";
                    }else{
                        bean.nodeModules.add(str);
                    }
                    break;
                case 28:
                    int counter = 1;
                    StringBuilder builder = new StringBuilder();
                    char c;
                    while(counter > 0){
                        c = indexer.getNextChar();
                        if (c == '(')
                            counter ++;
                        else if (c == ')')
                            counter --;
                        builder.append(c);
                    }
                    str += builder.toString();
                    state = 29;
                    break;
                case 29:
                    bean.nodeModules.add(str);
                    state = 4;
                    break;
                case 30:
                    str += analyseTextualConventionOutline(kit);
                    bean.nodeModules.add(str);
                    state = 31;
                    break;
                case 31:
                    state = 4;
                    break;
                case 32:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypes(MibKeys.ASSIGNMENT);
                    if ((state = kit.notNull(temp, 33, -32)) == 33){
                        str += temp;
                    }
                    break;
                case 33:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypes("}");
                    if ((state = kit.notNull(temp, 34, -33)) == 34){
                        str += temp;
                    }
                    break;
                case 34:
                    state = 4;
                    break;
                default:
                    kit.ThrowAnalysisException(errorInfo, state, str);
            }
        }
    }

    private String analyseTextualConventionOutline(AnalyserIndexerKit kit) {
        LetterIndexer indexer = kit.getIndexer();
        int state = 4;
        String str = "";
        String temp = null;
        while (state != 99){
            switch (state){
                case 1:
                    str = indexer.getNextIgnoreLeftSpace(CharType.Letter, CharType.Number, CharType.Hyphen);
                    state = kit.notNull(str, 2, -1);
                    break;
                case 2:
                    state = kit.nextMatch(MibKeys.ASSIGNMENT, 3, -2);
                    break;
                case 3:
                    state = kit.nextMatch(MibKeys.TEXTUAL_CONVENTION, 4, -3);
                    break;
                case 4:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypes(MibKeys.SYNTAX);
                    if ((state = kit.notNull(temp, 5, -4)) == 5){
                        str += temp;
                    }
                    break;
                case 5:
                    temp = kit.matchSyntaxType();
                    if ((state = kit.notNull(temp, 6, -5)) == 6){
                        str += " " + temp;
                    }
                    break;
                case 6:
                    if ((state = kit.nextMatch("{", 7, 11)) == 7){
                        str += " {";
                    }else if ((state = kit.nextMatch("(", 9, 11)) == 9){
                        str += " (";
                    }
                    break;
                case 7:
                    temp = indexer.getNextUntilAndIncludeSpecifiedTypes("}");
                    if ((state = kit.notNull(temp, 8, -7)) == 6){
                        str += temp;
                    }
                    break;
                case 8:
                    state = 11;
                    break;
                case 9:
                    temp = kit.matchBrackets();
                    str += temp;
                    state = 10;
                    break;
                case 10:
                    state = 11;
                    break;
                case 11:
                    state = 99;
                    break;
                default:
                    kit.ThrowAnalysisException("外部节点TEXTUAL-CONVENTION解析异常。", state, str);
            }
        }
        return str;
    }

    /**
     * 分析IMPORTS模块
     */
    public void analyseImportsModule(){
        if (bean == null)
            throw new MibException("获取不到MIB文件的Module模块。");
        else if (bean.importsModule == null){
            return;
        }
        LetterIndexer indexer = new LetterIndexer(bean.importsModule);
        AnalyserIndexerKit kit = new AnalyserIndexerKit(indexer);

        String errorInfo = "解析异常。";
        HashMap<String, List<String>> moduleNodeMap = new HashMap<>();
        List<String> nodeList = null;
        String str = null;
        int state = 0;
        while(state != 99){
            switch (state){
                case 0:
                    if ((state = kit.nextMatch(MibKeys.IMPORTS, 1, -state))==2){
                    }
                    break;
                case 1:
                    nodeList = new ArrayList<>();
                    if ((state = kit.nextMatch(";", 99, -1))== 99){
                    }else{
                        str = indexer.getNextIgnoreLeftSpace(CharType.Number, CharType.Letter, CharType.Hyphen);
                        if (StringUtils.isNullOrWhiteSpace(str) || str.startsWith("FROM ")){
                            state = -state;
                        }else{
                            nodeList.add(str.trim());
                            state = 2;
                        }
                    }
                    break;
                case 2:
                    if ((state = kit.nextMatch(",", 3, -2)) == 3){
                    }else if ((state = kit.nextMatch("FROM", 4, -2)) == 4){
                    }
                    break;
                case 3:
                    str = indexer.getNextIgnoreLeftSpace(CharType.Number, CharType.Letter, CharType.Hyphen);
                    if (StringUtils.isNotNullOrWhiteSpace(str)){
                        nodeList.add(str);
                        state = 2;
                    }else{
                        state = -state;
                    }
                    break;
                case 4:
                    // 提取库名
                    str = indexer.getNextIgnoreLeftSpace(CharType.Number, CharType.Letter, CharType.Hyphen);
                    if (StringUtils.isNotNullOrWhiteSpace(str)){
                        state = 5;
                    }else{
                        state = -state;
                    }
                    break;
                case 5:
                    if (moduleNodeMap.containsKey(str)){
                        moduleNodeMap.get(str).addAll(nodeList);
                    }else{
                        moduleNodeMap.put(str, nodeList);
                    }
                    state = 1;
                    break;
                case 99: break;
                default:
                    kit.ThrowAnalysisException(errorInfo, state, str);
            }
        }

        this.module.importsModuleNodesMap = moduleNodeMap;
    }

    /**
     * 解析各大节点模块
     */
    public void analyseNodeModules(){
        List<String> nodeStrs = bean.nodeModules;
        LetterIndexer indexer;
        AnalyserIndexerKit kit;
        String name;

        dispatcher.init(this.module);
        module.nodes = new HashMap<>();
        for (String nodeStr : nodeStrs){
            indexer = new LetterIndexer(nodeStr);
            kit = new AnalyserIndexerKit(indexer);
            name = indexer.getNextToSpaceIgnoreLeftSpace(); // 跳过节点名
            String type = kit.matchSyntaxType(); // 尝试匹配基本类型
            INodeAnalyser analyser;
            type = type != null ? type :
                    indexer.getNextToSpaceIgnoreLeftSpace(); // 匹配不到基本类型则尝试获取下一个字符
            if (type == null){
                throw new MibException("解析节点类型失败！nodeStr=[" + nodeStr + "]");
            }else if (type.equals(StringUtils.join(" ", MibKeys.OBJECT, MibKeys.IDENTIFIER))){
                analyser = new MibObjectIdentifierAnalyser(txtList);
            }else if ((analyser = this.dispatcher.assign(type)) == null){
                System.out.println("无法解析节点类型[" + name + "][" + type + "]");
                continue;
            }

            MibNode node = analyser.analyse(kit);
            List<MibNode> parentNodes = analyser.getParentNodes();
            for (MibNode parent : parentNodes){
                addNodeToModule(parent);
            }
            addNodeToModule(node);
        }
    }

    private void addNodeToModule(MibNode node){
        if (!this.module.nodes.containsKey(node.name)){
            this.module.nodes.put(node.name, node);
        }
    }

    // 用于测试
    private void printModule(String str){
//        System.out.println(str);
    }

    /**
     * 移除所有注释和空行
     * @param content
     */
    public String RemoveAnnotationAndEmptyLine(String content) {
        char[] chars = content.toCharArray();
        boolean flag = true; // 等于true时表示不处于引号以内。
        StringBuilder builder = new StringBuilder();
        for (int i = 0, len = chars.length; i < len; i++)
        {
            char c = chars[i];
            if (c == '"'){
                flag = !flag;
            }
            if (flag){
                if (c == '-' && i+1 < len && chars[i+1] == '-'){
                    for (; i < len && chars[i] != '\n'; i++);
                }
            }
            builder.append(chars[i]);
        }

        return builder.toString().replaceAll("\\n+", "\n").trim();
    } // 移除所有注释和空行

    /**
     * 替换mib文件中的字符串部分
     * @param content
     * @return
     */
    public String ReplaceTextContent(String content) {
        Queue<Integer> beginQueue = new LinkedList<>();
        Queue<Integer> endQueue = new LinkedList<>();
        int flag = -1;  // 标志位
        char[] chars = content.toCharArray();
        StringBuilder contentBuilder = new StringBuilder(content);
        for (int i = chars.length - 1; i > -1; i--) {
            char c = chars[i];
            if (c == '"') {
                if (flag == -1) {
                    flag = i;
                }else {  // 开始记录字符串
                    txtList.add(contentBuilder.substring(i+1, flag));
                    contentBuilder.replace(i, flag+1, "#{STRINDEX:" + (txtList.size()-1) + "}");
                    flag = -1;
                } // 重置标志位，生成中间字符串
            }
        }
        if (flag > -1) throw new MibException("双引号数量为单数，分析失败！");
        return contentBuilder.toString();
    } // 替换掉所有字符串

    /**
     * 把多个空白字符转化为一个空格
     * @param txt
     * @return
     */
    public String TransferWhiteCharacterToSpace(String txt) {
        return txt.replaceAll("[\\t\\r\\s\\n ]+", " ");
    }
}
